
package Individual;

import Loader.BufferedData;
import java.util.ArrayList;
import java.util.BitSet;

public class Individual {

    //Array centroides of cluster (each centroid is BitSet)
    //centroid in chromosome isn't real value - it's only granularity
    public ArrayList<ArrayList<BitSet>> chromosome;

    //computed centroids - faster access to values
    public ArrayList<ArrayList<Float>> chromosomeVal;

    //Number of clusters = chromosome.size()
    public int numOfClusters;

    //hasn't to equal chromosome.get(0).size();
    //in future we can add other think, e.g. parameters of functions
    public int numOfFeatures;

    //Number of bites per value - granularity: (MaxVal-MinVal)/numOfBitsPerValue
    public int numOfBitsPerValue;

    //2^numOfBitsPerValue
    public int maxValue;

    public  BufferedData bufferedData;

    /* * * * * ROMEK * * * * * */

    // computed distances from each data point in bufferedData 
    // to their cluster centers ; EXPECTED GREAT EFFICIANCY

    public ArrayList<ArrayList<Float>> storedDistances;

    public ArrayList<Boolean> centroidChangedFlags;

    /* * * * * END ROMEK * * * * * */

    /*
     *  Constructors
     */
    /*
    public Individual() {

        this.numOfCluster = 0;
        this.numOfFeatures = 0;
        this.numOfBitsPerValue = 0;
        this.chromosome = null;
        this.chromosomeInt = null;
        this.maxValue = 0;
        
    }
    */
    
    public Individual(int numOfCluster, 
                      int numOfFeatures,
                      int numOfBitsPerValue,
                      BufferedData bd) {

        this.numOfClusters = numOfCluster;
        this.numOfFeatures = numOfFeatures;
        this.numOfBitsPerValue = numOfBitsPerValue;
        this.bufferedData = bd;
        this.maxValue = (int) Math.pow( 2, numOfBitsPerValue );

        chromosome = new ArrayList< ArrayList<BitSet> >(numOfCluster);
        chromosomeVal = new ArrayList< ArrayList<Float> >(numOfCluster);

        /* * * * * ROMEK * * * * * * */

        storedDistances = new ArrayList<ArrayList<Float>>(numOfCluster);

        for(int i = 0 ; i < numOfCluster; i++) {
            storedDistances.add(new ArrayList<Float>());
        }

        for(ArrayList<Float> aF : storedDistances) {
            aF.ensureCapacity(bd.dataSize);
            for(int i=0; i<bd.dataSize; i++) {
                aF.add(null);
            }
        }

        centroidChangedFlags = new ArrayList<Boolean>(numOfCluster);
        for(int i=0; i<numOfCluster; i++) {
            centroidChangedFlags.add(null);
        }

        /* * * * * END ROMEK * * * * * */

        for(int i = 0; i < numOfCluster; ++i) {

            ArrayList<BitSet> features = new ArrayList<BitSet>(numOfFeatures);
            ArrayList<Float> fVal = new ArrayList<Float>(numOfFeatures);

            for(int j = 0; j < numOfFeatures; ++j) {

                features.add(new BitSet(numOfBitsPerValue));
                fVal.add(new Float(0));
            }

            chromosome.add( features );
            chromosomeVal.add( fVal );

        }

        computeCentroids();

    }


    public Individual(Individual individual) {

        numOfClusters = individual.numOfClusters;
        numOfFeatures = individual.numOfFeatures;
        numOfBitsPerValue = individual.numOfBitsPerValue;
        maxValue = individual.maxValue;
        bufferedData = individual.bufferedData;

        chromosome = new ArrayList<ArrayList<BitSet>>(numOfClusters);
        chromosomeVal = new ArrayList< ArrayList<Float> >(numOfClusters);

         /* * * * * ROMEK * * * * * * */

        storedDistances = new ArrayList<ArrayList<Float>>(numOfClusters);

        for(int i = 0 ; i < numOfClusters; i++) {
            storedDistances.add(new ArrayList<Float>());
        }

        int cluster = 0;
        for(ArrayList<Float> aF : storedDistances) {
             aF.ensureCapacity(bufferedData.dataSize);

             for(int i=0; i < bufferedData.dataSize; i++) {
                aF.set(i, individual.storedDistances.get(cluster).get(i));
             }

             cluster++;
        }

        centroidChangedFlags = new ArrayList<Boolean>(numOfClusters);

        for(int i=0; i<numOfClusters; i++) {
            centroidChangedFlags.add(false);
        }

        /* * * * * END ROMEK * * * * * */

        for(int i = 0; i < numOfClusters; ++i) {

            ArrayList<BitSet> features = new ArrayList<BitSet>(numOfFeatures);
            ArrayList<Float> fVal = new ArrayList<Float>(numOfFeatures);

            for(int j = 0; j < numOfFeatures; ++j) {

                BitSet t = (BitSet) individual.chromosome.get(i).get(j).clone();
                features.add( t );
                fVal.add( individual.chromosomeVal.get(i).get(j) );
                
            }

            chromosome.add( features );
            chromosomeVal.add( fVal );

        }

        computeCentroids();

    }


    public int getValue(int noClaster, int noFeature) {

        int result = 0;
        BitSet val = chromosome.get(noClaster).get(noFeature);

        for(int i = 0; i < val.size(); ++i)
            if(val.get(i))
                result += Math.pow(2, i);

        return result;

    }


    public void setValue(int noClaster, int noFeature, int value) {

        BitSet val = chromosome.get(noClaster).get(noFeature);

        for(int i = 0; i < val.size(); ++i)
            val.set( i, value%2 != 0 );

    }

    public void computeCentroids() {

        for(int i = 0; i < numOfClusters; ++i) {

            boolean centroidChanged = false;

            for(int j = 0; j < numOfFeatures; ++j) {

                Float val = getValue(i, j) * 
                            bufferedData.granularity[j] +
                            bufferedData.minValues[j];

                /* * * * * ROMEK * * * * * * */
                if( chromosomeVal.get(i).get(j) != null ) {
                    // check if the new computed value is different than the
                    // last one; if so, mark this down
                    if(chromosomeVal.get(i).get(j) != val) {
                        centroidChanged = true;
                        chromosomeVal.get(i).set(j, val);
                    }                    
                }
            }

            // check if at least one feature of this cluster has changed
            if(centroidChanged) {
                centroidChangedFlags.set(i, true);
            }
            else {
                centroidChangedFlags.set(i, false);
            }

            /* * * * * END ROMEK * * * * * */
        }
        
    }

}
